<?php
/**
 * @brief Klasa encyjna Offer
 * 
 * Obiekty tej klasy reprezentują oferty składane przez uzytkowników w czasie
 * trwania aukcji oraz oferty wyjściowe, składane wraz z starem aukcji. Z każdą 
 * powiązana jest wiązka pewnej liczby komponentów, czyli przyporządkowanych do 
 * oferty towarów wraz z wspólczynnikami, determinującymi ich liczbę w wiązce.
 */
class Application_Model_Offer
{
    protected $_Offer_id; /*!< Identyfikator oferty. */
    protected $_Auction_id; /*!< Identyfikator aukcji do której jest przyporządkowana oferta. */
    protected $_MarketEntity_id; /*!< Identyfikator użytkownika. */
    protected $_minVolume; /*!< Najmniejsza liczba wiązek, aby oferta została przyjęta.  */
    protected $_maxVolume; /*!< Maksimum wiązek, jakie mogą zostać przehanlowane. */
    protected $_offeredPrice; /*!< Oferowana cena za wiązkę. */
    protected $_acceptedVolume; /*!< Zaakceptowana liczba jednostek, po bilansowaniu. */
    protected $_firstOffer; /*!< Meta-informacja, determinująca czy oferta jest ofertą wyjściową. */
    protected $headOffer_id;
    protected $ownerGroup_id;


    protected $_components; /*!< Komponenty wiązki. */
    protected $_status; /*!< Aktualny status oferty.*/
    protected $_endtime; /*!< Czas zakończenia trwania aktualnego statusu. */
    protected $_groups;


    /**
     * @brief Konstruktor 
     * 
     * Przyjmuje tablicę znaków i na jej podstawie uzupełnia pola,
     * przy pomocy setOptions.
     * @param array $options 
     */
    public function __construct(array $options = null) {
        if (is_array($options)) {
            $this->setOptions($options);
        }
        $this->_groups = Array();
    }

    /**
     * @brief Metoda uzupełniająca pola na podstawie wejściowej tablicy.
     * 
     * @param array $options
     * @return Obiekt tej klasy.
     */
    public function setOptions(array $options) {

        $methods = get_class_methods($this);

        foreach ($options as $key => $value) {

            $method = 'set' . ucfirst($key);

            if (in_array($method, $methods)) {

                $this->$method($value);
            }
        }
        return $this;
    }
    /**
     * @brief Metoda zwracająca identikator oferty.
     *
     * @return int
     */
    public function getOffer_id() {
        return $this->_Offer_id;
    }
    /**
     * @brief Metoda do ustawiania identyfikatora oferty.
     * 
     * @param int $_Offer_id 
     */
    public function setOffer_id($_Offer_id) {
        $this->_Offer_id = $_Offer_id;
    }
    /**
     * @brief Metoda zwracająca identyfikator aukcji, powiązanej z ofertą.
     *
     * @return int.
     */
    public function getAuction_id() {
        return $this->_Auction_id;
    }
    /**
     * @brief Metoda do ustawiania identyfikatora aukcji, powiązanej z ofertą.
     *
     * @param int $_Auction_id 
     */
    public function setAuction_id($_Auction_id) {
        $this->_Auction_id = $_Auction_id;
    }
    /**
     * @brief Metoda zwracająca nazwę jednostki rynkowej, składającej ofertę.
     *
     * @return string 
     */
    public function getMarketEntity_id() {
        return $this->_MarketEntity_id;
    }
    /**
     * @brief Metoda do ustawiania nazwy jednostki rynkowej.
     *
     * @param string $_MarketEntity_id 
     */
    public function setMarketEntity_id($_MarketEntity_id) {
        $this->_MarketEntity_id = $_MarketEntity_id;
    }
    /**
     * @brief Metoda zwracająca minimalną przewidzianą liczbę wiązek.
     *
     * @return int
     */
    public function getMinVolume() {
        return $this->_minVolume;
    }
    /**
     * @brief Metoda do ustawiania minimalnej liczby wiązek.
     *
     * @param int $_minVolume 
     */
    public function setMinVolume($_minVolume) {
        $this->_minVolume = $_minVolume;
    }
    /**
     * @brief Metoda zwracająca maksymalną liczbę wiązek przewidzianych w ofercie.
     *
     * @return int 
     */
    public function getMaxVolume() {
        return $this->_maxVolume;
    }
    /**
     * @brief Metoda do ustawiania maksymalnej liczby wiązek przewidzianych w ofercie.
     *
     * @param int $_maxVolume 
     */
    public function setMaxVolume($_maxVolume) {
        $this->_maxVolume = $_maxVolume;
    }
    /**
     * @brief Metoda zwracająca zaoferowaną cenę za jedną wiązkę.
     *
     * @return int 
     */
    public function getOfferedPrice() {
        return $this->_offeredPrice;
    }
    /**
     * @brief Metoda do ustawiania zaoferowanej ceny za jedną wiązkę.
     *
     * @param int $_offeredPrice 
     */
    public function setOfferedPrice($_offeredPrice) {
        $this->_offeredPrice = $_offeredPrice;
    }
    /**
     * @brief Metoda zwracająca liczbę przyjętych ofert.
     *
     * @return int 
     */
    public function getAcceptedVolume() {
        return $this->_acceptedVolume;
    }
    /**
     * @brief Metoda do ustawiania liczby przyjętych ofert.
     *
     * @param int $_acceptedVolume 
     */
    public function setAcceptedVolume($_acceptedVolume) {
        $this->_acceptedVolume = $_acceptedVolume;
    }
    /**
     * @brief Metoda zwracająca meta-informacje o rodzaju oferty.
     *
     * @return 1 jak oferta wyjściowa, 0 w przeciwnym wypadku
     */
    public function getFirstOffer() {
        return $this->_firstOffer;
    }
    /**
     * @brief Metoda do ustawiania meta-informację o rodzaju oferty.
     *
     * @param int $_firstOffer 
     */
    public function setFirstOffer($_firstOffer) {
        $this->_firstOffer = $_firstOffer;
    }
    /**
     * @brief Metoda zwracająca listę komponentów wiązki powiązanej z ofertą.
     *
     * @return array Tablica obiektów Application_Model_OfferComponent
     */
    public function getComponents() {
        if($this->_components == NULL){
            try {
                $this->setComponents();
            } catch (Exception $exc) {
                echo $exc->getTraceAsString();
            }
        }
        return $this->_components;
    }
    /**
     * @brief Metoda do ustawiania listy komponentów wiązki powiązanej z ofertą.
     * 
     * Lista jest tworzona na podstawie danych z bazy danych z wykorzystaniem
     * klasy mapującej Application_Model_OfferComponentMapper.
     */
    public function setComponents() {
        $mapper = new Application_Model_OfferComponentMapper();
        if($this->_Offer_id == NULL) throw new Exception('Nie można wykonać tej operacji na pustym obiekcie');
        $components = $mapper->fetchComponentsByOffer($this->_Offer_id);
        $this->_components = $components;
    }
    
    /**
     * @brief Metoda zwracająca status oferty.
     *
     * @return 'Otwarta' | 'Zamknięta' 
     */
    public function getStatus() {
        return $this->_status;
    }
    /**
     * @brief Metoda do ustawiania statusu oferty.
     *
     * @param string $_status 
     */
    public function setStatus($_status) {
        $this->_status = $_status;
    }
    /**
     * @brief Metoda zwracająca datę i czas zakończenia obecnego statusu aukcji.
     *
     * @return string Format YYYY-MM-dd HH:ss
     */
    public function getEndTime() {
        return $this->_endtime;
    }
    /**
     * @brief Metoda do ustawiania daty i czasu zakończenia obecnego statusu aukcji.
     *
     * @param string $_endtime 
     */
    public function setEndTime($_endtime) {
        $this->_endtime = $_endtime;
    }
    
    /**
     * @brief Metoda zwracająca nazwę aukcji z którą jest powiązana oferta.
     *
     * @return string
     */
    public function getAuctionTitle() {
        $mapper = new Application_Model_AuctionMapper();
        $auction = $mapper->find($this->_Auction_id);
        return $auction->getTitle();
    }
    
    public function addGroup($group) {
        $this->_groups[] = $group;
    }
    
    /**
     * @brief Metoda zwracająca id oferty nadrzednej
     *
     */
    public function getHeadOffer_id() {
        if($this->_firstOffer == 1) {
            return $this->_Offer_id;
        }
        return $this->headOffer_id;
    }

    /**
     * @brief Metoda ustawiajaca id oferty nadrzednej
     *
     */
    public function setHeadOffer_id($headOffer_id) {
        $this->headOffer_id = $headOffer_id;
    }

    /**
     * @brief Metoda do zwrócenia id grupy do której należy oferta
     *
     */
    public function getOwnerGroup_id() {
        return $this->ownerGroup_id;
    }

    /**
     * @brief Metoda do ustawienia id grupy do której należy oferta
     *
     */
    public function setOwnerGroup_id($ownerGroup_id) {
        $this->ownerGroup_id = $ownerGroup_id;
    }
    
    /**
     * @brief Metoda sprawdzajaca czy mozna edytowac oferte
     *
     */
    public function offerOpen() {
        $auctionMapper = new Application_Model_AuctionMapper();
        $auctionId = $this->getAuction_id();
        $auction = $auctionMapper->find($auctionId);
        $end = $auction->getEndTime();
        $endTime= new Zend_Date($end);
        if(!empty($end) && $endTime->isEarlier(Zend_Date::now()))
            return false;
        return true;
    }
    
    /**
     * @brief Metoda zwracjaca listę grup zaleznych od oferty
     *
     * Dane są pobierane z bazy danych przez mappery
     */
    public function getGroups() {
        if(empty($this->_groups))
        {
            $this->setGroups();
        }
        return $this->_groups;
    }

     /**
     * @brief Metoda do ustawiania listy grup
     *
     * Dane są pobierane z bazy danych przez mappery
     */
    public function setGroups() {
        $offerId = $this->_Offer_id;
        $groupMapper = new Application_Model_GroupMapper();
        $groups = $groupMapper->fetchByOfferId($offerId);
        $this->_groups = $groups;
    }
    
     /**
     * @brief Metoda zwracjaca liste ofert które mozna z nią polaczyć
     *
     */
    public function getCopyableGroups() {
        
        if($this->_firstOffer == 1){
            return array();
        }
        
        $groupMapper = new Application_Model_GroupMapper();
        $ownerGroup = $groupMapper->find($this->ownerGroup_id);
        $offers = $ownerGroup->getOffers();
        $afillatedGroups = $this->getGroups();
        $excludedGroups = array();
        $results = array();
        foreach ($afillatedGroups as $group) {
            $excludedGroups[] = $group->getGroup_id();
        }
        foreach ($offers as $offer){
            $groups = $offer->getGroups();
            $ids = array();
            foreach ($groups as $group) {
                $ids[] = $group->getGroup_id();
                
            }
            $results = array_merge($results, array_diff($ids, $excludedGroups));
            $excludedGroups = array_unique(array_merge($ids, $excludedGroups));
        }
        //return $excludedGroups;
        return $results;
    }

    /**
     * @brief Metoda generująca M3XML danej oferty
     *
     * Generowane są pozostale towary i powiazane wirtualne oferty
     */
    public function toXML($info,$volume = NULL) {
        if($this->_firstOffer){
            $maxVolume = $this->getMaxVolume();
        } else {
            $maxVolume = $volume;
        }
        $commodities = '';
        $groups = $this->getGroups();
        $xml = '
                <m3:Offer id="o'.$this->getOffer_id().'" offeredPrice="'.$this->getOfferedPrice().'">
                    <m3:description>Offer subbmited by consumer '.$this->getMarketEntity_id().'</m3:description>
                    <m3:offeredBy ref="'.$this->getMarketEntity_id().'" />
                    <m3:volumeRange maxValue="'.$maxVolume.'" minValue="0" />
                    ';
             $components  = $this->getComponents();
             $xml.= '<m3:BundledOffer>  
                       ';
             foreach ($components as $component){
                if($component->getQuantity()){
                $xml .='<m3:offeredCommodity ref="com'.
                        $component->getCommodity_id().'" shareFactor="'.
                        $component->getShareFactor()*$component->getQuantity()*-1 .
                        '" />
                       ';
                }
            }
           foreach ($groups as $group) {
               $xml .='<m3:offeredCommodity ref="pvcO'.$this->_Offer_id.
                        'G'.$group->getGroup_id().'" shareFactor="1" />
                      ';
               $commodities .= $this->getCommodityXML($group->getGroup_id());
           }
           if($this->_firstOffer) {
                $xml .='<m3:offeredCommodity ref="vce'.$this->_Offer_id.
                        '" shareFactor="-1" />
                     ';
           }
           if(!empty($this->ownerGroup_id)) {
               $xml .='<m3:offeredCommodity ref="vcg'.$this->ownerGroup_id.
                        '" shareFactor="-1" />
                     ';
           }
           $xml .= '</m3:BundledOffer>
               ';
       $xml .= '</m3:Offer>
               ';
       $info['offers'] .= $xml;
       $info['commodities'] .= $commodities;
       if($this->_firstOffer) {
           for($i = 1; $i <= $maxVolume; $i++){
               $info['offers'] .= $this->getExcludingOffer($i);
           }
           $info['commodities'] .= $this->getCommodityXML();
       }
       foreach ($groups as $group) {
           $info = $group->toXML($info, $this->_Offer_id,$maxVolume);
       }
       return $info;
    }
    
    /**
     * @brief Metoda generująca M3XML wierualnych towarów połączenia oferta-grupa
     *
     */
    private function getCommodityXML($groupId = null) {
        if($groupId == null){
            $id = 'vce'.$this->_Offer_id;
            $msg = '
               <m3:Commodity dref="v" id="'.$id.'">
                 <m3:description>Virtual excluding commodity before offer '
                    .$this->_Offer_id.'</m3:description>';
        } else {
            $id = 'pvcO'.$this->_Offer_id.'G'.$groupId;
            $msg = '
               <m3:Commodity dref="v" id="'.$id.'">
                 <m3:description>Virtual commodity for group '
                        .$groupId.'and offer '.$this->_Offer_id.'</m3:description>';
        }

         $msg .='
               </m3:Commodity>
             ';
         return $msg;
    }
    
    
    /**
     * @brief Metoda generująca M3XML oferty aktywującej
     *
     */
    private function getExcludingOffer($count) {
        $xml = '
                <m3:Offer id="ov'.$this->getOffer_id().'e'.$count.'" offeredPrice="0">
                    <m3:description>Virtual excluding offer consumer '.$this->getMarketEntity_id().'</m3:description>
                    <m3:offeredBy ref="'.$this->getMarketEntity_id().'" />
                    <m3:volumeRange maxValue="1" minValue="0" />
                    <m3:BundledOffer>
                        <m3:offeredCommodity ref="vct'.$this->_MarketEntity_id.
                        '" shareFactor="-1" />
                        <m3:offeredCommodity ref="vce'.$this->_Offer_id.
                        '" shareFactor="'.$count.'" />
                    </m3:BundledOffer>
                </m3:Offer>
                ';
        return $xml;
    }

}

